#include"circle_buffer.h"
#include"thread.h"
#include"memory.h"
#include"debug.h"

static bool is_empty(circle_buffer*);
static bool is_full(circle_buffer*);
static void circle_wait(task_tcb**);
static void circle_wakeup(task_tcb**);

void circle_buffer_init(circle_buffer* pb,uint32_t capacity){
	pb->data_buffer = (uint8_t*)sys_malloc(capacity);
	pb->buffer_capacity = capacity;
	pb->head=pb->tail=pb->size=0;
	pb->consumer=pb->producer=NULL;
	init_lock(&pb->cir_lock);
}

static bool is_empty(circle_buffer* pb){
	return pb->size == 0;
}

static bool is_full(circle_buffer* pb){
	return pb->size==pb->buffer_capacity;
}


static void circle_wait(task_tcb** ptask){
	ASSERT(ptask!=NULL && *ptask==NULL);	
	*ptask=current_thread();
	thread_block(TASK_BLOCKED);
}

static void circle_wakeup(task_tcb** ptask){
	ASSERT(ptask!=NULL && *ptask!=NULL);
	thread_unblock(*ptask);	
	*ptask=NULL;
}

void put(circle_buffer* pb,int8_t data){
	while(is_full(pb)){
		lock_acquire(&pb->cir_lock);
		circle_wait(&pb->producer);
		lock_release(&pb->cir_lock);
	}

	pb->data_buffer[pb->head]=data;
	pb->head=(pb->head+1)%pb->buffer_capacity;
	pb->size++;

	if(pb->consumer!=NULL){
		circle_wakeup(&pb->consumer);
	}
}

uint8_t get(circle_buffer* pb){
	int8_t data;
	while(is_empty(pb)){
		lock_acquire(&pb->cir_lock);
		circle_wait(&pb->consumer);
		lock_release(&pb->cir_lock);
	}
	data=pb->data_buffer[pb->tail];
	pb->tail = (pb->tail+1)%pb->buffer_capacity;
	pb->size--;
	if(pb->producer!=NULL){
		circle_wakeup(&pb->producer);
	}
	return data;
}

void release_circle_buffer(circle_buffer* pb){
	sys_free(pb->data_buffer);
}
